using Fusion.Utilities.Singleton;
using System;
using System.Collections.Generic;
using System.Threading;

namespace Fusion.Async.Threads
{
    public enum ThreadType
    { 
        Normal,
        Persistent,
        Timer
    }


    public class ThreadData
    {
        private Thread thread;
        public Thread Thread { get => thread; }
        public bool IsAlive { get => isAlive; }

        Action persistentAction = null;

        private bool isAlive = false;
        private bool managed = false;

        private Timer timer;

        public ThreadData(Action action, ThreadType threadType, bool managed)
        {
            if (action != null)
            {
                isAlive = true;
                persistentAction = action;
                if (threadType == ThreadType.Normal)
                {
                    thread = new Thread(Run);
                }
                else if (threadType == ThreadType.Persistent)
                {
                    thread = new Thread(PersistentRun);
                }
                else if (threadType == ThreadType.Timer)
                {
                    timer = new Timer(new TimerCallback(TimerRun), null, -1, -1);
                }
                this.managed = managed;
                if (managed)
                {
                    ThreadManager.Instance.Add(this);
                }
            }
        }

        private void Run()
        {
            persistentAction();
            lock (this)
            {
                isAlive = false;
            }
        }

        private void PersistentRun()
        {
            while (isAlive)
            {
                persistentAction();
            }
        }

        private void TimerRun(object o)
        {
            persistentAction();
        }

        public void Start(int duaTime = -1, int period = -1)
        {
            thread?.Start();
            timer?.Change(duaTime, period);
        }

        public void Abort()
        {
            lock (this)
            {
                isAlive = false;
            }
            timer?.Dispose();
        }

        public void Release()
        {
            Abort();
            if (managed)
            {
                ThreadManager.Instance?.Remove(this);
            }
        }
    }

    public class ThreadManager : SingletonBehaviour<ThreadManager>
    {
        private List<ThreadData> threads = new List<ThreadData>();

        public void Remove(ThreadData threadData)
        {
            threads.Remove(threadData);
        }

        public void Add(ThreadData threadData)
        {
            threads.Add(threadData);
        }

        public void Clear()
        { 
            for (int index = 0; index < threads.Count; index++)
            {
                threads[index].Abort();
            }
            threads.Clear();
        }

        private void OnApplicationQuit()
        {
            Clear();
        }
    }
}

